package com.mars.common.fegin;


import com.ag.utils.DateUtils;
import com.google.common.collect.Lists;
import com.mars.common.utils.Jacksons;
import feign.Contract;
import feign.Feign;
import feign.Logger;
import feign.QueryMapEncoder;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import feign.querymap.BeanQueryMapEncoder;
import okhttp3.ConnectionPool;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignFormatterRegistrar;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.expression.ParseException;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignConfiguration {
	@Autowired(required = false)
    private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();

	@Bean 
	public QueryMapEncoder queryMapEncoder() {
		return new BeanQueryMapEncoder();
	}
	
	@Bean
	public FeignFormatterRegistrar feignFormatterRegistrar() {
		return new FeignFormatterRegistrar() {
			@Override
			public void registerFormatters(FormatterRegistry formatterRegistry) {
				formatterRegistry.addConverter(new DateToStringConverter());
				formatterRegistry.addFormatter(new DateFormatter());
			}
		};
	}
	
	class DateToStringConverter implements Converter<Date, String> {
	    @Override
	    public String convert(Date source) {
	        return DateUtils.formartDate(source, DateUtils.DEFAULT_DATE_FORMAT);
	    }
	}
	
	class DateFormatter implements Formatter<Date> {
	    @Override
	    public Date parse(String text, Locale locale) throws ParseException {
	        return DateUtils.parse(text);
	    }
	    @Override
	    public String print(Date date, Locale locale) {
	        return DateUtils.formartDate(date, DateUtils.DEFAULT_DATE_FORMAT);
	    }
	}
	@Bean
	public Encoder feignEncoder() {
		MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = Jacksons.jackson2HttpMessageConverter();
		ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(false, Lists.newArrayList(jackson2HttpMessageConverter));
		return new SpringFormEncoder(new CustomSpringEncoder(objectFactory));
	}
	
	@Bean
	public Decoder feignDecoder() {
		MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = Jacksons.jackson2HttpMessageConverter();
		ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(false, Lists.newArrayList(jackson2HttpMessageConverter));
		return new ResponseEntityDecoder(new SpringDecoder(objectFactory));
	}

	@Bean
	public FeignErrorDecoder feignErrorDecoder() {
		return new FeignErrorDecoder();
	}
	
	/**
	 * 日志级别
	 *
	 * @return
	 */
	@Bean
	public Logger.Level feignLoggerLevel() {
		return Logger.Level.FULL;
	}
	
	/**
	 * 使用okhttp3 
	 * @return
	 */
	@Bean
    public okhttp3.OkHttpClient okHttpClient(){
        return new okhttp3.OkHttpClient.Builder()
                .readTimeout(60, TimeUnit.SECONDS)
                .connectTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(120, TimeUnit.SECONDS)
                .connectionPool(new ConnectionPool())
                .build();
    }

	 @Bean
	 public Contract feignContract(FormattingConversionService feignConversionService) {
		//在原配置类中是用ConversionService类型的参数，但ConversionService接口不支持addConverter操作，使用FormattingConversionService仍然可以实现feignContract配置。
        feignConversionService.addConverter(new DateToStringConverter());
        return new SpringMvcContract(this.parameterProcessors, feignConversionService);
	 }
}
